package com.sky.aspect;

import com.sky.annotation.AutoFill;
import com.sky.constant.AutoFillConstant;
import com.sky.context.BaseContext;
import com.sky.enumeration.OperationType;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Controller;
import org.yaml.snakeyaml.events.Event;

import java.lang.reflect.Method;
import java.time.LocalDateTime;

/**
 * 自定义切面，实现公共字段自动填充处理逻辑
 */
@Aspect
@Controller
@Slf4j
public class AutoFillAspect {

    /**
     * 切入点,注解表示,两个条件都要满足切入点才生效
     * 分别表示任意返回值,com.sky.mapper包下的任意类,任意方法可以有任意参数
     * 第二个条件是,方法必须要有@AutoFill这个(自定义注解),写上注解的绝对路径
     */
    @Pointcut("execution(* com.sky.mapper.*.*(..)) " +
            "&& @annotation(com.sky.annotation.AutoFill)")
    public void autoFillPointCut(){}

    /**
     * 使用前置通知,在目标mapper执行前进行公共字段填充这样才符合逻辑
     */
    @Before("autoFillPointCut()")
    public void autoFill(JoinPoint joinPoint){
        log.info("前置通知进行公共字段填充");

        //1.获取到当前被拦截方法上的数据操作类型
        //1.1获取方法签名,方法签名是指包括方法名在内的方法声明，由方法的修饰符、返回值类型、方法名和参数列表组成
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();

        //获取方法上的自动以注解对象
        AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);
        //获取自定义注解中操作类型
        OperationType operationType = autoFill.value();
        //2.获取当前被拦截的方法参数--实体类对象
        //2.1 获取参数,并判断如果参数为空方法错误直接返回,修改和添加都是需要参数做条件的
        Object[] args = joinPoint.getArgs();
        if (args == null || args.length == 0){
            return;//如果args没有值直接返回因为新增修改方法必须有参数
        }
        //2.2 获取第一个参数，可能是实体类对象也有可能是其他对象,这里是因为我们知道只有一个参数才这样做
        Object entity = args[0];

        //提前定义属性赋值参数,也就是要添加的值
        LocalDateTime now = LocalDateTime.now();
        Long userId = BaseContext.getCurrentId();

        //判断方法是修改还是新增
        if (operationType == OperationType.INSERT){
            try {
                //是新增方法
                //获取实体类set方法进行赋值
                //创建时间
                Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
                //修改时间
                Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
                //创建人
                Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER,Long.class);
                //修改人
                Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER,Long.class);

                /**
                 *  3.2 为获取的以上四个set方法进行赋值
                 *  invoke(Object obj, Object... args)
                 */
                setCreateTime.invoke(entity,now);
                setUpdateTime.invoke(entity,now);
                setCreateUser.invoke(entity,userId);
                setUpdateUser.invoke(entity,userId);


            } catch (Exception e) {
                e.printStackTrace();
            }
        }else if (operationType == OperationType.UPDATE){

            try {
                //修改方法只需要获取两个set方法,修改人和修改时间,从实体类通过反射获取方法
                //获取改变修改人方法
                Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
                //获取改变修改时间方法
                Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);

                //通过获取的实体类方法进行set修改用.invoke()
                setUpdateUser.invoke(entity,userId);
                setUpdateTime.invoke(entity,now);
            } catch (Exception e) {
                e.printStackTrace();
            }


        }


    }

}




